package com.ht.controller;



import cn.hutool.json.JSONUtil;
import com.ht.data.Result;
import com.ht.domain.bo.UserBO;
import com.ht.dto.User;
import com.ht.em.ResultEnum;
import com.ht.service.IUserService;
import com.ht.util.JwtUtil;
import com.ht.util.ShiroUtils;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author 代齐园
 * @since 2022-05-24
 */
@RestController
@Api("关于用户的登录注册,和后续修改密码之类的功能")//Swagger2的注解
@RequestMapping("/user")
public class UserController {
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private IUserService userService;

    @PostMapping("/login")
    //value为这个方法干什么用的
    @ApiOperation(value = "用户登录")
    //name的意思是传的参是谁，value的意思是传的是什么，required是否必填，paramType传参的方式
    @ApiImplicitParams({
            @ApiImplicitParam(name = "user",value = "用户实体类",required = true,paramType = "body"),
            @ApiImplicitParam(name = "session",value = "HttpSession",required = false)
    })
    public Result login(@RequestBody User user,HttpSession session){
        //创建subject对象
        Subject subject = SecurityUtils.getSubject();
        // 创建token
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
        //调用subject的login方法
        //执行登录功能
        try{
            subject.login(token);
        }catch (UnknownAccountException e){
            System.out.println("账号不存在");
            //用户名不存在
            return Result.response(ResultEnum.ERROR_NAME);
        }catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
            //密码错误
            return Result.response(ResultEnum.ERROR_PASSWORD);
        }
        User user1 = User.builder()
                .id(user.getId()).username(user.getUsername()).password(user.getPassword())//id、用户名、密码
                .phone(user.getPhone()).idCard(user.getIdCard())//手机号、身份证号
                .atwillOne(user.getAtwillOne()).atwillTwo(user.getAtwillTwo()).coupons(user.getCoupons())//备用字段
                .intergral(user.getIntergral()).creatTime(user.getCreatTime()).vipStatus(user.getVipStatus())//健康斗、 创建时间
                .surplusPrice(user.getSurplusPrice())//余额
                .build();
        //脱敏处理
        session.setAttribute("user",user1.getUsername());
        UserBO bo = new UserBO();
        BeanUtils.copyProperties(user1,bo);
        String json = JSONUtil.toJsonStr(bo);
        //用户的id，用户整体的数据，角色
        String token1 = new JwtUtil().createJwt(bo.getId().toString(), json, "admin");
        return Result.OK().setData(token1);
    }
    //发短信
    @PostMapping("/getCode/{phoneNum}")
    //value为这个方法干什么用的
    @ApiOperation(value = "发送短信")
    //name的意思是传的参是谁，value的意思是传的是什么，required是否必填，paramType传参的方式
    @ApiImplicitParam(name = "phoneNum",value = "手机号",required = true,paramType = "path")
    public Result getCode(@PathVariable String phoneNum){
        //TODO 切换redis连接
        rocketMQTemplate.convertAndSend("ht-code-topic", phoneNum);//发送MQ
        return Result.OK();
    }
    //注册
    @PostMapping("register")
    //value为这个方法干什么用的
    @ApiOperation(value = "注册")
    //name的意思是传的参是谁，value的意思是传的是什么，required是否必填，paramType传参的方式
    @ApiImplicitParam(name = "user",value = "用户注册信息实体类",required = true,paramType = "body")
    public Result register(@RequestBody User user){
        ValueOperations opsForValue = redisTemplate.opsForValue();
        //匹配redis中的key(手机号)
        if (!redisTemplate.hasKey(user.getPhone())){
            return Result.response(ResultEnum.DID_NOT_SEND);//未发送
        }
        //获取redis中的key
        String o = (String)opsForValue.get(user.getPhone());
        //如果传入的验证码和redis中的相等
        if (user.getMessageCode().equals(o)){
            //获取盐
            String salt = ShiroUtils.getSalt(6);
            String md5Password = ShiroUtils.getMD5Password(salt, user.getPassword());
            user.setPassword(md5Password);//加密
            user.setSalt(salt);
            userService.registerUser(user);
        }
        return Result.OK();
    }
    @RequiresAuthentication
    //value为这个方法干什么用的
    @ApiOperation(value = "退出登录")
    @GetMapping("logout")
    public Result logout(){
        Subject subject = SecurityUtils.getSubject();
        // 如果 isAuthenticated 为 false 证明不是登录过的，
        // 同时 isRememberd 为true 证明是没登陆直接通过记住我功能进来的
        if (subject.isAuthenticated()){
            subject.logout();// session 会销毁，在SessionListener监听session销毁，清理权限缓存
        }
        System.out.println("退出登录成功");
        return Result.OK().setData("退出登录");
    }
    @PutMapping("/updateUser")
    //value为这个方法干什么用的
    @ApiOperation(value = "修改密码")
    //name的意思是传的参是谁，value的意思是传的是什么，required是否必填，paramType传参的方式
    @ApiImplicitParams({
            @ApiImplicitParam(name = "token",value = "用户令牌",required = true,paramType = "header"),
            @ApiImplicitParam(name = "user1",value = "用户修改数据",required = true,paramType = "body")
    })
    public Result updateUser(@RequestHeader("AuthToken")String token, @RequestBody User user1){
        Claims claims = new JwtUtil().parseJwt(token);
        User user = JSONUtil.toBean(claims.getSubject(), User.class);//通过请求头获取登录用户
        //匹配redis中的key(手机号)
        if (!redisTemplate.hasKey(user.getPhone())){
            return Result.response(ResultEnum.DID_NOT_SEND);//未发送
        }
        ValueOperations opsForValue = redisTemplate.opsForValue();
        //获取redis中的value(验证码)
        String o = (String)opsForValue.get(user.getPhone());
        if (!user1.getMessageCode().equals(o)){//如果验证码一直
            return Result.response(ResultEnum.CODE_MISMATCHING);//验证码不一致
        }
        //获取盐
        String salt = ShiroUtils.getSalt(6);
        String md5Password = ShiroUtils.getMD5Password(salt, user1.getPassword());
        user1.setPassword(md5Password);//密码加密
        user1.setSalt(salt);
        userService.updatePassword(user1);
        return Result.OK().setData("密码修改成功");
    }

    @DeleteMapping("/delete")
    @ApiOperation(value = "通过用户注册的手机号删除账号")
    //name的意思是传的参是谁，value的意思是传的是什么，required是否必填，paramType传参的方式
    @ApiImplicitParams({
            @ApiImplicitParam(name = "token",value = "用户令牌",required = true,paramType = "header"),
            @ApiImplicitParam(name = "phoneNum",value = "用户手机号",required = true,paramType = "query"),
            @ApiImplicitParam(name = "messageCode",value = "手机验证码",required = true,paramType = "query")
    })
    public Result delete(@RequestHeader("AuthToken")String token,@RequestParam("phoneNum") String phoneNum,@RequestParam("messageCode")String messageCode){
        Claims claims = new JwtUtil().parseJwt(token);
        User user = JSONUtil.toBean(claims.getSubject(), User.class);//通过请求头获取登录用户
        //匹配redis中的key(手机号)
        if (!redisTemplate.hasKey(user.getPhone())){
            return Result.response(ResultEnum.DID_NOT_SEND);//未发送
        }
        ValueOperations opsForValue = redisTemplate.opsForValue();
        //获取redis中的value(验证码)
        String o = (String)opsForValue.get(user.getPhone());
        if (!messageCode.equals(o)){//如果验证码一致
            return Result.response(ResultEnum.CODE_MISMATCHING);//验证码不一致
        }
        userService.deleteByIdPhone(phoneNum);
        return Result.OK();
    }

    @GetMapping("/balance")
    //value为这个方法干什么用的
    @ApiOperation(value = "查询用户余额")
    //name是传的参是谁，value的意思是传的是什么，required是否必填，paramType传参的方式
    @ApiImplicitParam(name = "token",value = "用户token",required = true,paramType = "header")
    public Result getBalance(@RequestHeader("AuthToken")String token){
        Claims claims = new JwtUtil().parseJwt(token);
        User user = JSONUtil.toBean(claims.getSubject(), User.class);//通过请求头获取登录用户
        User user1 = userService.getBalance(user);
        return Result.OK().setData(user1);
    }

}